看雪论坛作者ID:Avacci
目标app为algorithmbase_10.apk。点击CHECK会有加密后的字符串,看样子是Base64编码:
输出的值由MainActivity类的静态方法encodeFromJni_10生成,该方法需要传入一个16个字符的随机字符串。encodeFromJni_10是一个native方法:
其实现应该在libnative-lib.so中:
在ida中打开so文件,查看encodeFromJni_10方法的具体实现。用findcrypt脚本定位到base64编码表:既然能被识别,那应该是个标准的编码表,先从logcat中得到一批参数密文对:为了方便对更多输入值进行确认,写了个frida脚本,将encodeFromJni_10执行后的值和调用Base64.encodeToString后的值进行比较,查看是否相等。
多次测试后发现确实都相等,确定就是标准的Base64编码。
目标app为algorithmbase_11.apk.打开测试后,和之前的algorithmbase_10.apk结果十分相似,也像是用Base64编码:直接定位其so中的encodeFromJni_111方法实现.但离返回值最近的方法(后面做内存回收的不算)调用中可以发现类似base64编码表的内容:双击查看完整内容:
猜测应该是非标准的base64编码,使用了自定义的编码表,将编码表复制到CyberChef中(还要加上等号):
目标app为algorithmbase_12.apk。还是直接查看so文件中对应native方法encodeFromJni_112的实现。发现与之前的111极为相似。而加密的关键函数内部的实现,除了使用的编码表外一模一样。
按理说就是使用了另一种自定义编码表的base64编码才对。但是提取出其编码表并放入CyberChef中测试,发现密文结果并不一致。
会不会是base64编码完后还有什么步骤?还是说传入的参数在base64编码之前有被修改呢?为了确定这点,用frida hook了做base64操作的函数,输出其执行前后参数的内容。
发现传入的第二个参数就是原始生成的随机字符串,而函数执行完后第一个参数中存放的内容就是最后打印出来的密文。
看来没有什么额外加密步骤。那唯一的变数就是编码表的内容了。会不会是运行的时候编码表被改了呢。直接用一段脚本打印运行时内存中的编码表内容。
将这段编码表内容放入CyberChef中,就可以得到正确的结果了:
那么编码表是何时被修改的呢?在ida中查找下编码表的引用:发现有一处指向了另外一个之前没关注到的函数内部。很明显编码表就是在这里做了修改。
这个方法也是在encodeFromJni_112方法比较前期的地方就被调用。
目标app为algorithmbase_13.apk.直接去观察so中native方法encodeFromJni_113用到的编码表:
和12用到的一样,不过测试发现用这个表得到的结果和打印出来的值不一致。猜测又是运行时被修改了。将这部分内容作为编码表,到CyberChef上测试,发现提示编码表长度不对。检查后发现CyberChef中的base64编码表对‘-’的位置有要求,如果单纯的放在两个字母或者数字中间会被判定为表示区间的字符。可以用’\’进行转义:之前已经用frida hook脚本确定了所有加密只可能在函数sub_8b04中完成,那问题肯定还在这个函数中。在ida中对照12和13两个apk的sub_8b04函数,发现13的sub_8b04函数中有两行代码与12不一致,多了一步异或操作。这里异或的变量v4值等于传入的第三个参数的值,也就是待编码内容的长度。看来是base64编码的另一种魔改方式,这在CyberChef上应该是模拟不了了,需要自己实现一段代码。
只需要在标准的base64编码实现上,修改对应的地方即可。
看雪ID:Avacci
https://bbs.pediy.com/user-home-879855.htm
*本文由看雪论坛 Avacci 原创,转载请注明来自看雪社区